package com.itxiuyixiu.tools.util.map;

import java.time.Instant;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.*;

/**
 * @author 黄磊
 * @date 2020/4/21
 */
public class QueueScheduleHashMapImpl<K, V> extends ConcurrentHashMap<K, V> {

    private class TimeMap {
        K key;
        Instant overTime;

        public TimeMap(K key, Instant overTime) {
            this.key = key;
            this.overTime = overTime;
        }
    }

    private ConcurrentLinkedQueue<TimeMap> deleteKeyQueue;

    public QueueScheduleHashMapImpl() {
        startSchedule();
    }

    public QueueScheduleHashMapImpl(Map<? extends K, ? extends V> m) {
        super(m);
        startSchedule();
    }

    public QueueScheduleHashMapImpl(int initialCapacity) {
        super(initialCapacity);
        startSchedule();
    }

    public QueueScheduleHashMapImpl(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);
        startSchedule();
    }

    public QueueScheduleHashMapImpl(int initialCapacity, float loadFactor, int concurrencyLevel) {
        super(initialCapacity, loadFactor, concurrencyLevel);
        startSchedule();
    }

    private void startSchedule() {
        ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
        this.deleteKeyQueue = new ConcurrentLinkedQueue<>();
        scheduledExecutor.scheduleWithFixedDelay(() -> {
            System.out.println(deleteKeyQueue.size());
            deleteKeyQueue.removeIf(map -> {
                if (map.overTime != null && Instant.now().isAfter(map.overTime)) {
                    remove(map.key);
                    return true;
                }
                return false;
            });
        }, 0, 1, TimeUnit.SECONDS);
    }

    private void addTimeTemp(K key, long timeout, TimeUnit timeUnit) {
        V v = get(key);
        if (v == null) {
            deleteKeyQueue.add(new TimeMap(key, Instant.ofEpochMilli(Instant.now().toEpochMilli() + timeUnit.toMillis(timeout))));
        } else {
            deleteKeyQueue.forEach(e -> {
                if (Objects.equals(key, e.key)) {
                    e.overTime = Instant.ofEpochMilli(Instant.now().toEpochMilli() + timeUnit.toMillis(timeout));
                }
            });
        }
    }

    @Override
    public V put(K key, V value) {
        return this.put(key, value, 99999, TimeUnit.DAYS);
    }

    public V put(K key, V value, long timeout, TimeUnit timeUnit) {
        addTimeTemp(key, timeout, timeUnit);
        return super.put(key, value);
    }


}
